using System;
using System.Collections.Generic;
using UIFramework.Core;
using UnityEngine;

namespace UIFramework.Window
{
    public class WindowUILayer : UILayer<IWindowController>
    {
        // 弹窗层
        [SerializeField] private WindowParaLayer priorityParaLayer = null;
        
        // 当前窗口
        public IWindowController CurrentWindow {get; private set;}

        // 保存队列
        private Queue<WindowHistoryEntry> windowQueue;
        // 历史记录栈
        private Stack<WindowHistoryEntry> windowHistory;

        public event Action RequestScreenBlock;
        public event Action RequestScreenUnblock;
        
        private bool IsScreenTransitionInProgress 
        {
            get { return screensTransitioning.Count != 0; }
        }

        private HashSet<IScreenController> screensTransitioning;

        /// <summary>
        /// 初始化
        /// </summary>
        public override void Initialize()
        {
            base.Initialize();
            registeredScreens = new Dictionary<string, IWindowController>();
            windowQueue = new Queue<WindowHistoryEntry>();
            windowHistory = new Stack<WindowHistoryEntry>();
            screensTransitioning = new HashSet<IScreenController>();
        }
        
        /// <summary>
        /// 重写注册方法
        /// </summary>
        /// <param name="screenId"></param>
        /// <param name="controller"></param>
        protected override void ProcessScreenRegister(string screenId, IWindowController controller)
        {
            base.ProcessScreenRegister(screenId, controller);
            controller.InTransitionFinished += OnInAnimationFinished;
            controller.OutTransitionFinished += OnOutAnimationFinished;
            controller.CloseRequest += OnCloseRequestedByWindow;
        }

        /// <summary>
        /// 重写取消注册方法
        /// </summary>
        /// <param name="screenId"></param>
        /// <param name="controller"></param>
        protected override void ProcessScreenUnregister(string screenId, IWindowController controller) 
        {
            base.ProcessScreenUnregister(screenId, controller);
            controller.InTransitionFinished -= OnInAnimationFinished;
            controller.OutTransitionFinished -= OnOutAnimationFinished;
            controller.CloseRequest -= OnCloseRequestedByWindow;
        }
        
        /// <summary>
        /// 重写显示窗口方法
        /// </summary>
        /// <param name="screen"></param>
        /// <exception cref="NotImplementedException"></exception>
        public override void ShowScreen(IWindowController screen)
        {
            ShowScreen<IWindowProperties>(screen, null);
        }
        
        public override void ShowScreen<TProps>(IWindowController screen, TProps properties)
        {
            IWindowProperties windowProp =  properties as IWindowProperties;
            
            // 是否应该进队列
            if (ShouldEnqueue(screen, windowProp)) 
            {
                Debug.Log("进队列" + screen.ScreenId);
                EnqueueWindow(screen, properties);
            }
            else // 直接显示，不进队列
            {
                DoShow(screen, windowProp);
            }
        }

        /// <summary>
        /// 重写隐藏窗口方法
        /// </summary>
        /// <param name="screen"></param>
        public override void HideScreen(IWindowController screen)
        {
            if (screen == CurrentWindow) // 是否是当前窗口
            {
                // 移出历时栈
                windowHistory.Pop();
                AddTransition(screen);
                screen.Hide();

                CurrentWindow = null;

                if (windowQueue.Count > 0) // 当前队列是否为空
                {
                    ShowNextInQueue();
                }
                else if (windowHistory.Count > 0) // 历史栈否为空
                {
                    ShowPreviousInHistory();
                }
            }
            else 
            {
                Debug.LogError(string.Format(
                        "[WindowUILayer] Hide requested on WindowId {0} but that's not the currently open one ({1})! Ignoring request.",
                        screen.ScreenId, CurrentWindow != null ? CurrentWindow.ScreenId : "current is null"));
            }
        }
        
        /// <summary>
        /// 重写隐藏所有窗口方法
        /// </summary>
        /// <param name="shouldAnimateWhenHiding">是否需要动画</param>
        public override void HideAll(bool shouldAnimateWhenHiding = true) 
        {
            base.HideAll(shouldAnimateWhenHiding);
            CurrentWindow = null;
            priorityParaLayer.RefreshDarken();
            windowHistory.Clear();
        }
        
        /// <summary>
        /// 重新设置屏幕的父节点
        /// </summary>
        /// <param name="controller"></param>
        /// <param name="screenTransform"></param>
        public override void ReparentScreen(IScreenController controller, Transform screenTransform) 
        {
            IWindowController window = controller as IWindowController;

            if (window == null) 
            {
                Debug.LogError("[WindowUILayer] Screen " + screenTransform.name + " is not a Window!");
            }
            else 
            {
                // 是一个弹窗节点定义到弹窗层
                if (window.IsPopup) 
                {
                    priorityParaLayer.AddScreen(screenTransform);
                    return;
                }
            }

            base.ReparentScreen(controller, screenTransform);
        }

        /// <summary>
        /// 进队列
        /// </summary>
        /// <param name="screen"></param>
        /// <param name="properties"></param>
        /// <typeparam name="IProp"></typeparam>
        private void EnqueueWindow<IProp>(IWindowController screen, IProp properties) where IProp : IScreenProperties
        {
            windowQueue.Enqueue(new WindowHistoryEntry(screen, (IWindowProperties) properties));
        }

        /// <summary>
        /// 是否能够进队列
        /// </summary>
        /// <param name="screen"></param>
        /// <param name="properties"></param>
        /// <returns></returns>
        private bool ShouldEnqueue(IWindowController controller, IWindowProperties windowProp)
        {
            // 当前没有窗口且队列为空
            if (CurrentWindow == null && windowQueue.Count == 0)
            {
                return false;
            }

            // 弹出窗口需要进队列
            if (windowProp != null && windowProp.SuppressPrefabProperties)
            {
                return windowProp.WindowQueuePriority != WindowPriority.ForceForeground;
            }

            if (controller.WindowPriority != WindowPriority.ForceForeground)
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 显示上一个历史
        /// </summary>
        private void ShowPreviousInHistory()
        {
            if (windowHistory.Count > 0)
            {
                // 从历史记录栈中取出
                WindowHistoryEntry window = windowHistory.Pop();
                DoShow(window);
            }
        }

        /// <summary>
        /// 显示下一个队列历史
        /// </summary>
        private void ShowNextInQueue()
        {
            if (windowQueue.Count > 0)
            {
                // 从队列中取出
                WindowHistoryEntry window = windowQueue.Dequeue();
                DoShow(window);
            }
        }

        /// <summary>
        /// 显示窗口
        /// </summary>
        /// <param name="screen">需要显示的窗口</param>
        /// <param name="properties">携带的参数</param>
        private void DoShow(IWindowController screen, IWindowProperties properties)
        {
            DoShow(new  WindowHistoryEntry(screen, properties));
        }

        /// <summary>
        /// 显示窗口
        /// </summary>
        /// <param name="windowEntry">需要显示的窗口</param>
        private void DoShow(WindowHistoryEntry windowEntry) 
        {

            if (CurrentWindow == windowEntry.Screen) 
            {
                Debug.LogWarning(
                    string.Format(
                        "[WindowUILayer] 请求的WindowID ({0}) 已经打开重复操作", CurrentWindow.ScreenId));
            }
            // 当前存在窗口
            // 需要打开的窗口是一个运行前置的窗口
            // 并且不是弹出窗口
            else if (CurrentWindow != null
                     && CurrentWindow.HideOnForegroundLost
                     && !windowEntry.Screen.IsPopup) 
            {
                // 暂时隐藏当前窗口
                CurrentWindow.Hide();
            }
            // 新增：支持弹窗自动关闭
            else if (CurrentWindow != null 
                     && CurrentWindow.HideOnForegroundLost
                     && CurrentWindow.IsPopup
                     && windowEntry.Screen.IsPopup)
            {
                CurrentWindow.Hide();
            }

            // 加入历史栈
            windowHistory.Push(windowEntry);

            // 窗口进入过度状态
            AddTransition(windowEntry.Screen);

            // 弹出窗口特殊处理，打开黑暗背景
            if (windowEntry.Screen.IsPopup) 
            {
                priorityParaLayer.DarkenBG();
            }
            
            // 展示需要展示的窗口
            windowEntry.Show();

            // 更新当前窗口引用
            CurrentWindow = windowEntry.Screen;
        }
        
        /// <summary>
        /// 窗口进入动画完成时调用
        /// </summary>
        /// <param name="screen"></param>
        private void OnInAnimationFinished(IScreenController screen) 
        {
            RemoveTransition(screen);
        }

        /// <summary>
        /// 窗口退出动画完成时调用
        /// </summary>
        /// <param name="screen"></param>
        private void OnOutAnimationFinished(IScreenController screen) 
        {
            RemoveTransition(screen);
            var window = screen as IWindowController;
            if (window.IsPopup) 
            {
                priorityParaLayer.RefreshDarken();
            }
        }

        /// <summary>
        /// 窗口请求关闭时触发
        /// </summary>
        /// <param name="screen"></param>
        private void OnCloseRequestedByWindow(IScreenController screen) 
        {
            HideScreen(screen as IWindowController);
        }

        private void AddTransition(IScreenController screen) 
        {
            screensTransitioning.Add(screen);
            if (RequestScreenBlock != null) 
            {
                RequestScreenBlock();
            }
        }
        
        private void RemoveTransition(IScreenController screen) 
        {
            screensTransitioning.Remove(screen);
            if (!IsScreenTransitionInProgress) {
                if (RequestScreenUnblock != null) {
                    RequestScreenUnblock();
                }
            }
        }

    }
}